昨天成功處理了 1、2、3,但 4 輸出 "IIII" 而非 "IV"。今天用 TDD 處理羅馬數字的減法規則。
核心規則:
建立 tests/day12/roman-numerals.test.ts
:
import { describe, it, expect } from 'vitest'
import { toRoman } from '../../src/roman/romanNumerals'
describe('Roman Numerals Converter', () => {
it('converts 1 to I', () => {
expect(toRoman(1)).toBe('I')
})
it('converts 2 to II', () => {
expect(toRoman(2)).toBe('II')
})
it('converts 3 to III', () => {
expect(toRoman(3)).toBe('III')
})
it('converts 4 to IV', () => {
expect(toRoman(4)).toBe('IV')
})
})
測試失敗 🔴:Expected: "IV", Received: "IIII"
更新 src/roman/romanNumerals.ts
:
export function toRoman(num: number): string {
if (num === 4) return 'IV'
let result = ''
for (let i = 0; i < num; i++) {
result += 'I'
}
return result
}
測試通過! 🟢
加入測試:
it('converts 5 to V', () => {
expect(toRoman(5)).toBe('V')
})
修改實作:
export function toRoman(num: number): string {
if (num === 5) return 'V'
if (num === 4) return 'IV'
let result = ''
for (let i = 0; i < num; i++) {
result += 'I'
}
return result
}
加入測試:
it('converts 6 to VI', () => {
expect(toRoman(6)).toBe('VI')
})
it('converts 7 to VII', () => {
expect(toRoman(7)).toBe('VII')
})
it('converts 8 to VIII', () => {
expect(toRoman(8)).toBe('VIII')
})
重構 實作:
export function toRoman(num: number): string {
let result = ''
if (num === 4) {
return 'IV'
}
if (num >= 5) {
result += 'V'
num -= 5
}
for (let i = 0; i < num; i++) {
result += 'I'
}
return result
}
測試通過! ✅
加入測試:
it('converts 9 to IX', () => {
expect(toRoman(9)).toBe('IX')
})
測試失敗 🔴:輸出 "VIIII" 而非 "IX"
更新 實作:
export function toRoman(num: number): string {
let result = ''
if (num === 9) return 'IX'
if (num === 4) return 'IV'
if (num >= 5) {
result += 'V'
num -= 5
}
for (let i = 0; i < num; i++) {
result += 'I'
}
return result
}
加入測試:
it('converts 10 to X', () => {
expect(toRoman(10)).toBe('X')
})
更新 實作:
export function toRoman(num: number): string {
let result = ''
if (num === 10) return 'X'
if (num === 9) return 'IX'
if (num === 4) return 'IV'
if (num >= 5) {
result += 'V'
num -= 5
}
for (let i = 0; i < num; i++) {
result += 'I'
}
return result
}
測試通過! 🎉
重構 成映射表形式:
export function toRoman(num: number): string {
const mappings = [
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I']
]
let result = ''
for (const [value, symbol] of mappings) {
while (num >= value) {
result += symbol
num -= value
}
}
return result
}
透過 TDD,我們發現羅馬數字的重要模式:
完整實作 src/roman/romanNumerals.ts
:
export function toRoman(num: number): string {
const mappings = [
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I']
]
let result = ''
for (const [value, symbol] of mappings) {
while (num >= value) {
result += symbol
num -= value
}
}
return result
}
完整測試 tests/day12/roman-numerals.test.ts
:
import { describe, it, expect } from 'vitest'
import { toRoman } from '../../src/roman/romanNumerals'
describe('Roman Numerals Converter', () => {
const testCases = [
[1, 'I'], [2, 'II'], [3, 'III'], [4, 'IV'], [5, 'V'],
[6, 'VI'], [7, 'VII'], [8, 'VIII'], [9, 'IX'], [10, 'X']
]
testCases.forEach(([num, expected]) => {
it(`converts ${num} to ${expected}`, () => {
expect(toRoman(num)).toBe(expected)
})
})
})
執行測試:
npm test tests/day12
所有測試通過!我們有了處理 1-10 的羅馬數字轉換器。
試著思考以下問題:
今天我們成功實作了 1-10 的羅馬數字轉換,從最簡單的 if-else 開始,逐步演進到優雅的映射表模式。這就是 TDD 的魅力 - 讓我們的程式碼隨著測試逐步進化。
每個測試都像是一個小小的里程碑,紅燈告訴我們方向,綠燈確認我們走對了路,重構讓我們的程式碼更加優雅。這就是 TDD 的節奏感!
今天的 TDD 之旅就到這裡,明天我們繼續深入探索! 🚀